/*
 * Copyright 2007 Dan Hodge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.datajanitor.baja.spi.data;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.datajanitor.baja.BoxException;
import net.datajanitor.baja.data.FileInfo;

/**
 * Represents the complete tree of files and directories in a user's Box account
 */
public class FileInfoTree {
    private FileInfoNode root;
    
    private static class FileInfoNode {
        private final FileInfo node;
        private final FileInfo parent;
        private final List<FileInfoNode> children;
        
        private FileInfoNode(FileInfo parent, FileInfo node) {
            this.parent = parent;
            this.node = node;
            this.children = new ArrayList<FileInfoNode>();
        }
        
        private void accept(FileInfo.Visitor visitor) {
            visitor.visit(this.node);
            for (FileInfoNode child : this.children) {
                child.accept(visitor);
            }
        }
    }
    
    private FileInfoTree() {
    }
    
    private FileInfoNode getNodeById(int id) {
        FileInfoNode node = getNodeById(this.root, id);
        if (node == null) {
            throw new BoxException("No node with id: " + id + " found in tree");
        }
        
        return node;
    }
    
    private FileInfoNode getNodeById(FileInfoNode cur, int id) {
        FileInfoNode node = null;
        
        if (cur.node.getId() == id) {
            node = cur;
        } else {
            if (cur.node.isDirectory()) {
                for (Iterator<FileInfoNode> i = cur.children.iterator(); node == null && i.hasNext(); ) {
                    node = getNodeById(i.next(), id);
                }
            }
        }
        
        return node;
    }
    
    private void acceptAt(FileInfoNode base, FileInfo.Visitor visitor) {
        base.accept(visitor);
    }
    
    public static FileInfoTree newFileInfoTree(int rootId, String rootName) {
        // chicken-egg dependency between FileInfoTree and root FileInfo node, this is the 
        // best thing I could think of
        FileInfoTree tree = new FileInfoTree();
        tree.root = new FileInfoNode(null, new FileInfo(rootId, rootName, true, tree));
        
        return tree;
    }
        
    public FileInfo getRoot() {
        return this.root.node;
    }
    
    public FileInfo getParent(FileInfo info) throws BoxException {
        return getNodeById(info.getId()).parent;
    }
    
    public List<FileInfo> getChildren(FileInfo info) throws BoxException {
        FileInfoNode node = getNodeById(info.getId());

        List<FileInfo> children = new ArrayList<FileInfo>(node.children.size());
        for (FileInfoNode child : node.children) {
            children.add(child.node);
        }

        return children;
    }
    
    public void addFileInfo(FileInfo parent, FileInfo newFile) throws BoxException {
        FileInfoNode node = getNodeById(parent.getId());
        node.children.add(new FileInfoNode(node.node, newFile));
    }
    
    public void accept(FileInfo.Visitor visitor) {
        acceptAt(this.root, visitor);    
    }
    
    public void acceptAt(FileInfo base, FileInfo.Visitor visitor) throws BoxException {
        acceptAt(getNodeById(base.getId()), visitor);
    }    
}
